#pragma once

#include "Common.hpp"
#include "CentralCache.hpp"
#include <iostream>
#include <pthread.h>

class ThreadCache
{
public:
    void* Allocate(size_t size)// 线程申请size大小的空间
    {
        cout << "debug ThreadCache Allocate begin" << endl;
        assert(size <= MAX_BYTES);

        size_t alig_size = SizeClass::RoundUp(size);//获得对齐后的字节数
        size_t index = SizeClass::Index(size); //获得该字节数对应的哈希桶的下标

        if(!_freelist[index].Empty())//说明此时自由链表上有空间
        {
            return _freelist[index].Pop();
        }
        else//说明此时对应的自由链表上没有空间 
        {
            // cout << "debug ThreadCache Allocate freelist without space" << endl;
            return FetchFromCentralCache(index, alig_size);
        }
        // cout << "debug ThreadCache Allocate end" << endl;
    }
    void Deallocate(void* obj, size_t size)// 回收线程中大小为size的obj空间
    {
        assert(obj);//回收obj的空间前提时obj要有空间
        assert(size <= MAX_BYTES);

        size_t index = SizeClass::Index(size);

        _freelist[index].Push(obj);//将obj的空间还回给自由链表

        if(_freelist[index].Size() >= _freelist[index].MaxSize())
        {
            //由于本项目是简化版的tcmalloc，所以这里的归还的规则也需要简化
            //当_freelist所管理的空间超过其一次性最多能提供的空间的时候，就像CentralCache归还MaxSize块空间
            ListTooLong(_freelist[index], size);//归还空间
        }
    }
    void* FetchFromCentralCache(size_t index, size_t align_size)//当哈希桶中空间不够的时候向Central Cache中获取空间
    {
        cout << "debug ThreadCache FetchFromCentralCache begin" << endl;
        //这里分配的空间块的数量是根据NumMoveSize和MaxSize来决定的
        //Size::NumMoveSize(align_size)：当前最多能够批的align_size大小的块的最大数量
        //_freelist[index].MaxSize()：在自由链表_freelist在index这个位置处，单次申请的块未达到上限时，最多能够申请块的数量
        size_t batch_num = std::min(SizeClass::NumMoveSize(align_size), _freelist[index].MaxSize());

        if(batch_num == _freelist[index].MaxSize())
        {
            //未达到上限时，下次多申请一块空间
            _freelist[index].MaxSize()++;//满开始反馈调节的核心
        }

        void* start = nullptr;
        void* end = nullptr;

        // cout << "debug ThreadCache FetchFromCentralCache FetchRangeObj test" << endl;
        size_t actual_num = CentralCache::GetInstance()->FetchRangeObj(start, end, align_size, batch_num);//返回值一定大于等于1
        // cout << "debug ThreadCache FetchFromCentralCache FetchRangeObj end" << endl;

        assert(actual_num >= 1);

        //该函数既要给线程提供内存块，还要将多的空间块挂到对应的freelist上
        if(actual_num == 1)
        {
            //此时，CentralCache只提供了一个空间块，所以就直接将这个空间块返回
            assert(start == end);
            return start;
        }
        else
        {
            //此时，CentralCache提供了不止一个空间块
            //这时候将第一个空间块返回，剩下的空间块挂到对应的freelist上
            assert(start != end);
            _freelist[index].PushRange(ObjNext(start), end, actual_num-1/*因为有一块空间需要提供给线程，而剩下的空间才是链表的*/);

            return start;
        }
        // cout << "debug ThreadCache FetchFromCentralCache end" << endl;
    }
    void ListTooLong(FreeList& list, size_t size/*回收的每一块大小*/)//因为list管理的空间过多，所以需要将一部分空间归还给CentralCache
    {
        void* start = nullptr;
        void* end = nullptr;

        list.PopRange(start, end, list.MaxSize());//此时就通过start获得了这部分需要回收的空间

        CentralCache::GetInstance()->ReleaseListToSpans(start, size);
    }
private:
    FreeList _freelist[FREE_LIST_NUM]; //哈希，每个桶表示的就是自由链表
};

//tls，全称为thread local storage，是一种可以只让一个变量在自己线程内全程可见的技术
//linux gcc 的tls静态实现方式就是在一个变量之前加一个__thread进行修饰
static __thread ThreadCache* p_tls_thread_cache = nullptr;